﻿#include <QSqlQuery>
#include <QSqlError>
#include <QXmppPresence.h>

#include "../config/Config.h"
#include "../global/QmlNames.h"
#include "../util/Singleton.h"
#include "../persist/DbManager.h"
#include "RosterModel.h"

X_BEGIN_NAMESPACE

// 定义在ListView上显示的数据角色,注意与DataRolesNames保存同步
enum DataRoles{
    availableStatus,      // 在线状态
    bareJid,              // bareJid
    nickName,             // 昵称
    group,                // 所在分组
    description,
    email,
    address,
    remarkName,
    photo,
	thumbnail,
    photoType,
    url,
    telephone,
    mobile,
    birthday,
    DATA_ROLE_MAX
};

const char* RosterModel::DataRolesNames[] = {
    "availableStatus",
    "bareJid",
    "nickName",
    "group",
    "description",
    "email",
    "address",
    "remarkName",
    "photo",
	"thumbnail",
    "photoType",
    "url",
    "telephone",
    "mobile",
    "birthday"
};

const char RosterModel::READ_ROSTER[] = "select barejid, nick_name, remark_name, photo, photo_type, group_name from roster order by group_name, barejid";
const char RosterModel::INSERT_CONTACT[] = "insert into roster(barejid, group_name, photo, photo_type) values(:barejid, :group_name, :photo, :photo_type)";
const char RosterModel::UPDATE_CONTACT[] = "update roster set group_name = :group_name, photo = :photo, photo_type = :photo_type WHERE barejid = :barejid;";

const char RosterModel::READ_CONTACT_VCARD[] = "select first_name, last_name, email, url, telephone, mobile, address from roster where barejid = :barejid";
const char RosterModel::INSERT_CONTACT_VCARD[] = "insert into roster(barejid, first_name, last_name, birthday, email, url, telephone, mobile, address) values(:barejid, :first_name, :birthday, :last_name, :email, :url, :telephone, :mobile, :address)";
const char RosterModel::UPDATE_CONTACT_VCARD[] = "update roster set first_name = :first_name, last_name = :last_name, birthday = :birthday, email = :email, url = :url, telephone = :telephone, mobile = :mobile, address = :address where barejid = :barejid";

const char RosterModel::UPDATE_CONTACTS_GROUP[] = "update roster set group_name = :group_name where group_name = :group_name";

const char RosterModel::DELETE_CONTACT[] = "delete from roster where barejid = :barejid";

///////////////////////////////////////////////////////////
// RosterModel
///////////////////////////////////////////////////////////
RosterModel::RosterModel(QObject *parent)
    : QAbstractListModel(parent)
{
    for(int i = availableStatus; i < DATA_ROLE_MAX; ++i){
        m_roleNames.insert(i, DataRolesNames[i]);
    }
}

RosterModel &RosterModel::instance()
{
    return *Singleton<RosterModel>::instance(RosterModel::createInstance);
}

const char *RosterModel::qmlName()
{
    return QmlNames::TopLevelModelSingleton::RosterSingleton;
}

QObject *RosterModel::qmlInstance(QQmlEngine* engine, QJSEngine* scriptEngine)
{
    Q_UNUSED(engine);
    Q_UNUSED(scriptEngine);

    return &instance();
}

/**
 * @brief 创建模型实例。
 * @return
 */
RosterModel *RosterModel::createInstance()
{
    RosterModel* inst = new RosterModel;
    return inst;
}

/**
 * @brief 联系人排序器。不同组时，按组排序，同组时按JID排序。
 *
 * @param c1
 * @param c2
 * @return
 */
bool RosterModel::contactLessThan(const ContactModel *c1, const ContactModel *c2)
{
    if(c1->group() == c2->group()){
        return c1->bareJid() < c2->bareJid();
    }

	return c1->group() < c2->group();
}

/**
 * @brief 清理。
 */
void RosterModel::houseKeep()
{
	beginRemoveRows(QModelIndex(), 0, m_contacts.size() - 1);

	foreach (ContactModel* contact, m_contacts) {
		delete contact;
	}

	m_contacts.clear();
	m_contactsByBareJid.clear();

	endRemoveRows();
}

int RosterModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);

    return m_contacts.size();
}

QVariant RosterModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid()){
        return QVariant();
    }

    ContactModel* contact = m_contacts[index.row()];
    switch (role) {
        case availableStatus:
            return (int)contact->availableStatus();
            break;
        case bareJid:
            return contact->bareJid();
            break;
        case nickName:
            return contact->nickName();
            break;
        case group:
            return contact->group();
            break;
        case description:
            return contact->description();
            break;
        case email:
            return contact->email();
            break;
        case address:
            return contact->address();
            break;
        case remarkName:
            return contact->remarkName();
            break;
        case photo:
            return contact->photo();
            break;
		case thumbnail:
			return contact->thumbnail();
			break;
        case photoType:
            return (int)contact->photoType();
            break;
        case url:
            return contact->url();
            break;
        case telephone:
            return contact->telephone();
            break;
        case mobile:
            return contact->mobile();
        case birthday:
            return contact->birthday();
            break;
        default:
            return QVariant("Not Implemented.");
    }
}

QHash<int, QByteArray> RosterModel::roleNames() const
{
    return m_roleNames;
}

/**
 * @brief 增加联系人，仅在内存中，不保存到数据库。
 *
 * @param bareJid
 * @param group 所在分组
 * @return
 */
ContactModel &RosterModel::addContact(const QString &bareJid,
                                      const QString &groupName)
{
    ContactModel *contact = new ContactModel(bareJid);
    contact->setGroup(groupName);
    contact->setPhoto(Config::instance().defaultAvatar());
    contact->setAvailableStatus(AvailableStatus::OffLine);

    m_contactsByBareJid[bareJid] = contact;

    bool inserted = false;
    for(int i = 0; i < m_contacts.size(); ++i){
        if(contactLessThan(m_contacts[i], contact)) continue;

        m_contacts.insert(i, contact);
        inserted = true;
        break;
    }

    if(!inserted) m_contacts.push_back(contact);

    return *contact;
}

/**
 * @brief 增加并保存联系人到数据库。
 *
 * @param bareJid
 * @param groupName
 * @return
 */
ContactModel &RosterModel::saveContact(const QString &bareJid,
                                       const QString& photo,
                                       PhotoType photoType,
                                       const QString &groupName)
{
    // 已经存在，直接返回
    if(m_contactsByBareJid.contains(bareJid)){
        return *m_contactsByBareJid[bareJid];
    }

    // 先数据库插入
    QSqlQuery query = DbManager::instance().createQuery();
    query.prepare(INSERT_CONTACT);
    query.bindValue(":barejid", bareJid);
    query.bindValue(":group_name", groupName);
    query.bindValue(":photo", photo);
    query.bindValue(":photo_type", (int)photoType);
    if(!query.exec()){
        query.prepare(UPDATE_CONTACT);
        query.bindValue(":barejid", bareJid);
        query.bindValue(":group_name", groupName);
        query.bindValue(":photo", photo);
        query.bindValue(":photo_type", (int)photoType);
        if(!query.exec()){
            qDebug() << "Insert contact to roster failed for contact: " << bareJid << ", error: " << query.lastError().text();
        }
    }

    // 保存至联系人列表
    ContactModel &contact = addContact(bareJid, groupName);

    // 发送信号
    int index = m_contacts.indexOf(&contact);
    beginInsertRows(QModelIndex(), index, index);
    endInsertRows();

    return contact;
}

/**
 * @brief 更新联系人的名片。
 *
 * @param bareJid
 * @return
 */
void RosterModel::updateContactVCard(const QString &bareJid, const VCard& vcard)
{
    Q_ASSERT(&vcard != NULL);

    // :barejid, :first_name, :last_name, :email, :url, :telephone, :mobile, :address
	// :first_name, :last_name, :birthday, :email, :url, :telephone, :mobile, :address
    QSqlQuery query  = DbManager::instance().createQuery();
    query.prepare(UPDATE_CONTACT_VCARD);
    query.bindValue(":barejid", bareJid);
    query.bindValue(":first_name", vcard.m_firstName);
    query.bindValue(":last_name", vcard.m_lastName);
    query.bindValue(":birthday", vcard.m_birthday);
    query.bindValue(":email", vcard.m_email);
    query.bindValue(":url", vcard.m_url);
    query.bindValue(":telephone", vcard.m_telephone);
    query.bindValue(":mobile", vcard.m_mobile);
    query.bindValue(":address", vcard.m_address);

    if(!query.exec()){
		qDebug() << "SQL: " << UPDATE_CONTACT_VCARD;
        qDebug() << "Update contact vcard failed: " << query.lastError().text();
    }
}

/**
 * @brief 查找联系人。
 *
 * @param bareJid
 * @return
 */
QObject *RosterModel::findContact(const QString &bareJid) const
{
    QObject* obj = NULL;
    if(m_contactsByBareJid.contains(bareJid)){
        ContactModel* contact = m_contactsByBareJid[bareJid];
        obj = contact;
    }

    return obj;
}

/**
 * @brief 移除联系人。
 * @param bareJid
 */
void RosterModel::removeContact(const QString &bareJid)
{
    if(m_contactsByBareJid.contains(bareJid)){
        ContactModel* contact = m_contactsByBareJid[bareJid];
        m_contactsByBareJid.remove(bareJid);
        m_contacts.removeOne(contact);
    }
}

/**
 * @brief 加载电子名片。
 * @param bareJid
 * @return
 */
VCard *RosterModel::loadVCard(const QString &bareJid)
{
	VCard* vcard = new VCard;

    // first_name, last_name, email, url, telephone, mobile, address, description
    QSqlQuery query = DbManager::instance().createQuery();
    query.prepare(READ_CONTACT_VCARD);
    query.bindValue(":barejid", bareJid);
    query.exec();
    if(query.next()){
        vcard->m_firstName = query.value("first_name").toString();
        vcard->m_lastName = query.value("last_name").toString();
        vcard->m_email = query.value("email").toString();
        vcard->m_url = query.value("url").toString();
        vcard->m_telephone = query.value("telephone").toString();
        vcard->m_mobile = query.value("mobile").toString();
        vcard->m_address = query.value("address").toString();
	}

	return vcard;
}

/**
 * @brief 从数据库加载联系人名单
 */
void RosterModel::loadDataFromDb()
{
    try{
		//
        m_contacts.clear();
        m_contactsByBareJid.clear();

        QSqlQuery query = DbManager::instance().createQuery();
        query.exec(READ_ROSTER);

        // 名单初始化时，只加载联系人的几个关键字段，其他字段在需要时再加载，比如：显示联系人的名片时
        while (query.next()) {
			// barejid, nick_name, remark_name, photo, photo_type, group_name
			QString bareJid = query.value("barejid").toString();
			QString nickName = query.value("nick_name").toString();
			QString remarkName = query.value("remark_name").toString();
			QString photo = query.value("photo").toString();
			PhotoType photoType = PhotoType(query.value("photo_type").toInt());
			QString groupName = query.value("group_name").toString();

            addContact(bareJid, groupName)
                    .setNickName(nickName)
                    .setRemarkName(remarkName)
                    .setPhoto(photo)
                    .setPhotoType(photoType);
        }

		beginInsertRows(QModelIndex(), 0, m_contacts.size() - 1);
		endInsertRows();
    } catch(...){
        qDebug() << "Read roster from database failed.";
    }
}

X_END_NAMESPACE
